package com.iob.coin.blImpl.kgbulider;

import com.iob.coin.DAO.KGDAO;
import com.iob.coin.DAO.NodeDAO;
import com.iob.coin.DAO.TripleDAO;
import com.iob.coin.DAO.TypeDAO;
import com.iob.coin.bl.kgbuilder.KgBuilderService;
import com.iob.coin.po.KG;
import com.iob.coin.po.Node;
import com.iob.coin.po.Triple;
import com.iob.coin.po.Type;
import com.iob.coin.util.FileUtil;
import com.iob.coin.util.UserSessionUtil;
import com.iob.coin.util.logutil.Logger;
import com.iob.coin.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.transaction.Transactional;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

@Service
@Transactional
public class KgBuilderServiceImpl implements KgBuilderService {

    /**
     * !!!
     * 修改清单
     * 1. Controller中uploadFile方法与前端映射 (未确认)
     * 2. s_id,o_id含义改变 且 类型修改为Int (未确认)
     * 3. Node对象中增加x,y用于记录其布局信息,处理可能带来的改变 (未确认)
     * 4. 数据库中所有图谱id类型修改为int (未确认)
     * 5. 去除Triple类中Kgname属性 (未确认)
     * 6. KgInfoVO中增加Kgname属性 (未确认)
     * 7. fileutil中saveFile方法改变 (未确认)
     */

    @Autowired
    FileUtil fileUtil;

    @Autowired
    KGDAO kgDAO;

    @Autowired
    NodeDAO nodeDAO;

    @Autowired
    TripleDAO tripleDAO;

    @Autowired
    TypeDAO typeDAO;

    Logger logger = new Logger(this.getClass().getSimpleName(),System.out);

    @Override
    public ResponseVO newKG(String kgName) {
        int userid = UserSessionUtil.getLoginUserId();

        if(kgDAO.kgNameOfUser(userid).contains(kgName)){
            return ResponseVO.buildFailure("知识图谱名称重复");
        }

        KG savedKg = kgDAO.save(new KG(userid,kgName,""));

        return ResponseVO.buildSuccess(savedKg.getId());

    }

    @Override
    public ResponseVO uploadFile(MultipartFile multipartFile, String kgname) {
        int userid = UserSessionUtil.getLoginUserId();

        if(kgDAO.kgNameOfUser(userid).contains(kgname)){
           return ResponseVO.buildFailure("知识图谱名称重复");
        }

        String fileOriginalName = multipartFile.getOriginalFilename();
        if(fileOriginalName == null) return ResponseVO.buildFailure("未知错误，文件丢失！");

        //生成用户服务器文件夹
        String rootPath=System.getProperty("user.dir");
        String dirPath = rootPath + "\\data\\" + userid;
        File folder = new File(dirPath);
        if (!folder.exists() && !folder.isDirectory()) {
            folder.mkdirs();
        };

        //存储文件
        String fullPath = dirPath + "\\" +  System.currentTimeMillis() + "-" + fileOriginalName;
        if(!fileUtil.saveFile(multipartFile,fullPath)){
            return ResponseVO.buildFailure("未知错误，存储失败");
        }

        KG savedKg = kgDAO.save(new KG(userid,kgname,fullPath));

        //解析文件并存入数据库
        if(!writeInDatabase(savedKg.getId(), fullPath)){
            kgDAO.deleteById(savedKg.getId());
            return ResponseVO.buildFailure("未知错误，写入数据库失败");
        }

        logger.log("uploadFile","知识图谱创建",new HashMap<String,String>(){{
            put("id",String.valueOf(savedKg.getId()));
            put("name",savedKg.getName());
        }});

        //返回结果为产生知识图谱的id
        return ResponseVO.buildSuccess(savedKg.getId());
    }

    @Override
    public boolean writeInDatabase(int kgId,String filePath){
        /**
         * csv里存的是整个csv的信息
         * 一共五列 0:ID | 1:Type | 2:Predicate | 3:O_id | 4:label_ZH
         * 后续需要将所有的node和triple存进数据库中
         * node存入所有节点，去重
         */
        if(fileUtil.readFile(filePath)==null){
            return false;
        }
        ArrayList<String[]> csv=fileUtil.readFile(filePath);//读出csv
        //System.out.println(csv);
        ArrayList<String[]> nodeList=new ArrayList<>();

        HashSet<String> set=new HashSet<>();
        HashSet<String> typeSet=new HashSet<>();
        for(int row=0;row<csv.size();row++){

            //把表中所有的Type类存入数据库
            if(typeSet.add(csv.get(row)[1])){
                Type type=new Type(kgId,csv.get(row)[1],"",-1.0);
                typeDAO.save(type);
            }

            //  ID:node[0]  Type:node[1]  label_ZH:node[2]
            String[] node=new String[3];
            int index=0;
            //去重，不能有重复的ID
            if(set.add(csv.get(row)[0])) {
                for (int column = 0; column < csv.get(row).length; column++) {
                    //剔除表的第二列和第三列
                    if (column != 2&&column!=3) {
                        node[index] = csv.get(row)[column];
                        index++;
                    }
                }
                nodeList.add(node);
            }
        }
        /**
         * 写入数据库
         * String node[3];
         * ID:node[0]  Type:node[1]  label_ZH:node[2]
         * 初始存入node的坐标x和y都是-1,表示无效
         */
        for(int i=0;i<nodeList.size();i++){
            int id=typeDAO.findID(nodeList.get(i)[1],kgId);
            Node node1=new Node(nodeList.get(i)[0],id,nodeList.get(i)[2],kgId,-1.0,-1.0,"",-1.0);
            nodeDAO.save(node1);
        }

        /**
         * 一共五列 0:ID | 1:Type | 2:Predicate | 3:O_id | 4:label_ZH
         * String tri[3];
         * S_ID:tri[0]  Predicate:tri[1]  O_ID:tri[2]
         */
        ArrayList<String[]>tripleList=new ArrayList<>();
        for(int row=0;row<csv.size();row++){
            String[] tri=new String[3];
            int index=0;
            for(int column=0;column<csv.get(row).length;column++){
                if(column==0||column==2||column==3){
                    tri[index]=csv.get(row)[column];
                    index++;
                }
            }
            tripleList.add(tri);
        }

        for(int i=0;i<tripleList.size();i++){
            //根据kgId和节点的NodeAtId拿到节点在Node表中的id
            int s_id=nodeDAO.IdByNodeAtIdAndKgId(tripleList.get(i)[0],kgId);
            List<Node>a=nodeDAO.findNodeByKgIdAndNodeAtId(kgId,tripleList.get(i)[2]);
            //只有宾语在node表中存在的时候才会加入三元组，否则不加入三元组
            if(a.size()!=0){
                int o_id=a.get(0).getId();
                Triple triple=new Triple(s_id,tripleList.get(i)[1],o_id,kgId);
                tripleDAO.save(triple);
            }

        }
        return true;
    }

    @Override
    public ResponseVO createKg(int kgId) {
        //返回给前端的信息，包含一个NodeVO的list和一个TripleVO的list
        KgInfoVO kgInfoVO=new KgInfoVO();
        //NodeVO的list
        List<NodeVO>nodeVOS=new ArrayList<>();
        //取出node表里的kgId是传入id的所有Node记录
        List<Node>nodes=nodeDAO.findByKgID(kgId);

        //样式列表
        List<Type>types=typeDAO.findByKgID(kgId);
        List<TypeVO>typeVOS=new ArrayList<>();
        for (Type t:types){
            TypeVO typeVO=new TypeVO();
            typeVO.setId(t.getId());
            typeVO.setKgID(t.getKgID());
            typeVO.setType(t.getNode_type());
            typeVO.setColor(t.getColor());
            typeVO.setRadius(t.getRadius());
            typeVOS.add(typeVO);
        }
        kgInfoVO.setTypeVOS(typeVOS);

        for (Node node : nodes) {
            NodeVO nodeVO = new NodeVO();
//            System.out.println(node.getId());
            nodeVO.setId(node.getId());
            nodeVO.setNodeAtId(node.getNodeAtId());
            nodeVO.setType_id(node.getType_id());
           // nodeVO.setType(typeDAO.getNodeType(node.getId(), kgId));  //设置type
            nodeVO.setLabel_ZH(node.getLabel_ZH());
            nodeVO.setKgID(kgId);
            nodeVO.setHor(node.getX());
            nodeVO.setVer(node.getY());
            nodeVO.setColor(node.getColor());
            nodeVO.setRadius(node.getRadius());
            nodeVOS.add(nodeVO);
        }
        kgInfoVO.setNodeVOS(nodeVOS);

        //取出triple数据库里kgId是传入id所有三元组记录
        List<Triple>all=tripleDAO.findByKgID(kgId);
        //TripleVO的list
        List<TripleVO>tripleVOS=new ArrayList<>();
        for (Triple tri : all) {
            TripleVO tripleVO = new TripleVO();
            //int o_type_id=0;
            String o_label = "";

            //int s_type_id=nodeDAO.findById(tri.getS_id()).get(0).getType_id();
            String s_label = nodeDAO.findById(tri.getS_id()).get(0).getLabel_ZH();
            //如果这个三元组有o_id
            if (tri.getO_id() != -1) {
                //  o_type_id=nodeDAO.findById(tri.getO_id()).get(0).getType_id();
                o_label = nodeDAO.findById(tri.getO_id()).get(0).getLabel_ZH();
            }
            tripleVO.setId(tri.getId());
            tripleVO.setPredicate(tri.getPredicate());
            tripleVO.setS_id(tri.getS_id());
            tripleVO.setO_id(tri.getO_id());
            // tripleVO.setS_type_id(s_type_id);
            tripleVO.setS_label(s_label);
            //tripleVO.setO_type_id(o_type_id);
            tripleVO.setO_label(o_label);
            tripleVOS.add(tripleVO);
        }
        kgInfoVO.setTripleVOS(tripleVOS);
        //kg的ID
        kgInfoVO.setKgID(kgId);
        kgInfoVO.setKgName(kgDAO.findNameById(kgId));

        logger.log("createKg","知识图谱加载",new HashMap<String,String>(){{
            put("id",String.valueOf(kgInfoVO.getKgID()));
            put("name",kgInfoVO.getKgName());
        }});

        return ResponseVO.buildSuccess(kgInfoVO);
    }

    @Override
    public ResponseVO getNameAndIdList() {
        int userId = UserSessionUtil.getLoginUserId();

        List<String[]>re=new ArrayList<>();
        List<KG>kgs=kgDAO.findByUserId(userId);
        for (KG kg:kgs){
            String[] a=new String[2];
            a[0]=kg.getName();
            a[1]=String.valueOf(kg.getId());
            re.add(a);
        }

        logger.log("getNameAndIdList","获取图谱信息",new HashMap<String,String>(){{
            put("count",String.valueOf(kgs.size()));
        }});

        //System.out.println(re.get(0)[0]+re.get(0)[1]);
        return ResponseVO.buildSuccess(re);
    }

    @Override
    public ResponseVO getKgs() {
        int userId = UserSessionUtil.getLoginUserId();

        List<String[]>re=new ArrayList<>();
        List<KG>kgs=kgDAO.findByUserId(userId);

        return ResponseVO.buildSuccess(kgs);
    }

    @Override
    public ResponseVO deleteKg(int kgId) {

        tripleDAO.deleteByKgID(kgId);
        nodeDAO.deleteByKgID(kgId);
        typeDAO.deleteByKgID(kgId);
        String filepath= kgDAO.pathById(kgId);
        File file=new File(filepath);
        boolean flag=file.delete();

//        if(!flag){
//            return ResponseVO.buildFailure("未知错误");
//        }

        kgDAO.deleteById(kgId);//要先删除文件再删除kg表的信息，因为要在kg表中得到文件路径

        logger.log("deleteKg","知识图谱删除",new HashMap<String,String>(){{
            put("id",String.valueOf(kgId));
        }});

        return ResponseVO.buildSuccess();
    }


}
